package rtcp_app

import (
	"encoding/binary"
	"errors"
	"fmt"
	
	"github.com/pion/rtcp"
)

const (
	headerLength = 4
)

const (
	ssrcLength = 4
)

type App struct {
	Source uint32
	Name   [4]byte
	App    []byte
}

func (g App) Marshal() ([]byte, error) {
	/*
	 *        0                   1                   2                   3
	 *        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
	 *       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *       |V=2|P|    ST   |   PT=APP=204  |             length            |
	 *       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *       |                             SSRC                              |
	 *       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *       :                      name (ASCII) N                           :
	 *       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
	 *       |                application-dependent data A                 ...
	 *       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 */

	rawPacket := make([]byte, g.len())
	hData, err := g.Header().Marshal()
	if err != nil {
		return nil, err
	}
	copy(rawPacket, hData)

	offset := headerLength
	binary.BigEndian.PutUint32(rawPacket[offset:], g.Source)
	offset += ssrcLength

	copy(rawPacket[offset:], g.Name[:])
	offset += len(g.Name)

	copy(rawPacket[offset:], g.App)

	return rawPacket, nil
}

func (g *App) Unmarshal(rawPacket []byte) error {
	/*
	 *        0                   1                   2                   3
	 *        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
	 *       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *       |V=2|P|    ST   |   PT=APP=204  |             length            |
	 *       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *       |                             SSRC                              |
	 *       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *       :                      name (ASCII) N                           :
	 *       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
	 *       |                application-dependent data A                 ...
	 *       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 */

	var header rtcp.Header
	if err := header.Unmarshal(rawPacket); err != nil {
		return err
	}

	if header.Type != rtcp.TypeApplicationDefined {
		return errWrongType
	}

	if getPadding(len(rawPacket)) != 0 {
		return errPacketTooShort
	}

	offset := headerLength + ssrcLength
	g.Source = binary.BigEndian.Uint32(rawPacket[offset:])

	copy(g.Name[:], rawPacket[offset:offset+4])
	offset += 4

	g.App = rawPacket[offset:]

	return nil
}

// Header returns the Header associated with this packet.
func (g *App) Header() rtcp.Header {
	return rtcp.Header{
		Padding: false,
		Count:   uint8(1),
		Type:    rtcp.TypeApplicationDefined,
		Length:  uint16((g.len() / 4) - 1),
	}
}

func (g *App) len() int {
	appLen := (len(g.App) + 3) / 4 * 4
	l := headerLength + ssrcLength + len(g.Name) + appLen

	// align to 32-bit boundary
	return l + getPadding(l)
}

// DestinationSSRC returns an array of SSRC values that this packet refers to.
func (g *App) DestinationSSRC() []uint32 {
	out := make([]uint32, 1)
	out[0] = g.Source
	return out
}

func (g App) String() string {
	out := fmt.Sprintf("APP %v\n", g.Name)

	return out
}

// getPadding Returns the padding required to make the length a multiple of 4
func getPadding(packetLen int) int {
	if packetLen%4 == 0 {
		return 0
	}
	return 4 - (packetLen % 4)
}

var (
	errPacketTooShort = errors.New("rtcp: packet too short")
	errWrongType      = errors.New("rtcp: wrong packet type")
)
